package com.eric.seo;

import java.io.IOException;
import java.io.StringReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Attribute;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.wltea.analyzer.core.IKSegmenter;
import org.wltea.analyzer.core.Lexeme;

import com.alibaba.fastjson.JSONObject;
import com.eric.downloader.SimplePageDownloader;
import com.eric.utils.Reader;
import com.eric.utils.UrlUtils;
import com.eric.utils.page.AnalyticParser;
import com.eric.utils.page.IR;

public class SeoPage {

	private int SCRIPTS_BLOCK_LIMIT = 250;

	private String ROBOTSURL;

	private Document DOCUMENT;

	private JSONObject JSON = new JSONObject();

	private Elements LINKELEMENTS;

	private Elements ELEMENTS;

	private HashSet<String> uniqLinks = new HashSet<>();

	private HashSet<String> outLinks = new HashSet<>();

	private int STATUS;

	private String url;

	public static SeoPage downloadSeoPage(String url) {
		SimplePageDownloader spd = new SimplePageDownloader();
		Document doc = null;
		SeoPage page = null;
		try {
			doc = spd.download(url).document();
			if (doc != null) {
				page = new SeoPage(doc);
				page.status(spd.status());
				page.url(spd.realUrl());
			}
		} catch (NullPointerException e) {
			e.printStackTrace();
		}
		return page;
	}

	public SeoPage(Object input) {
		this.DOCUMENT = Jsoup.parse(Reader.read2String(input));

		if (DOCUMENT.baseUri() != null)
			url = DOCUMENT.baseUri();

		ELEMENTS = DOCUMENT.getAllElements();
		LINKELEMENTS = DOCUMENT.getElementsByTag("a");

		for (Element e : LINKELEMENTS) {
			String link = e.attr("href");
			if (!link.startsWith("http"))
				continue;

			if (UrlUtils.isExternal(link, DOCUMENT.baseUri()))
				outLinks.add(link);
			else
				uniqLinks.add(link);
		}
		toString();
	}

	public SeoPage(Document doc) {
		this.DOCUMENT = doc;

		if (DOCUMENT.baseUri() != null)
			url = DOCUMENT.baseUri();

		ELEMENTS = DOCUMENT.getAllElements();
		LINKELEMENTS = DOCUMENT.getElementsByTag("a");

		for (Element e : LINKELEMENTS) {
			String link = e.attr("href");
			if (!link.startsWith("http"))
				continue;

			if (UrlUtils.isExternal(link, DOCUMENT.baseUri()))
				outLinks.add(link);
			else
				uniqLinks.add(link);
		}
		toString();
	}

	/**
	 * SeoPage的摘要 用于概览和直接输出
	 */
	public String toString() {
		JSON.put("title", title());
		if (StringUtils.isNotBlank(url())) {
			JSON.put("url", url());
			JSON.put("protocol", protocol());
			JSON.put("status", status());
			JSON.put("host", host());
			JSON.put("root", root());
		}
		JSON.put("linksCount", links().size());
		JSON.put("outLinksCount", outLinks().size());
		JSON.put("imageCount", images().size());
		JSON.put("h1", h1());
		JSON.put("avgElementDepth", avgElementDepth());
		JSON.put("textCount", text().length());
		JSON.put("wordsCount", segWords().size());
		JSON.put("uniqWordsCount", uniqWords().size());
		JSON.put("scriptsCount", scriptsCount());
		JSON.put("largeBlockOfScripts", largeBlockOfScripts());
		JSON.put("maxElementDepth", maxElementDepth());
		JSON.put("isArticlePage", isArticlePage());

		return JSON.toJSONString();
	}

	public void url(String url) {
		this.url = url;
	}

	public String url() {
		return url;
	}

	public String host() {
		String host = null;
		try {
			host = new URL(url()).getHost();
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		}
		return host;
	}

	public String root() {
		return UrlUtils.getRoot(DOCUMENT.baseUri());
	}

	public String robotsLoc() {
		return UrlUtils.robotsUrl(DOCUMENT.baseUri());
	}

	public String title() {
		return DOCUMENT.title();
	}

	public Elements titleE() {
		return DOCUMENT.getElementsByTag("title");
	}

	public String description() {
		return DOCUMENT.getElementsByAttributeValue("name", "description").attr("content");
	}

	public Elements descriptionE() {
		return DOCUMENT.getElementsByAttributeValue("name", "description");
	}

	public int cssCount() {
		int i = 0;
		for (Element e : DOCUMENT.getAllElements()) {
			if (e.attr("rel").equals("stylesheet") || e.attr("type").equals("text/css"))
				i++;
		}
		return i;
	}

	public boolean largeBlockOfScripts() {
		Elements scripts = DOCUMENT.getElementsByTag("script");
		boolean b = false;
		for (Element e : scripts) {
			if (!e.hasAttr("src")) {
				if (e.data().length() > SCRIPTS_BLOCK_LIMIT)
					b = true;
			}
		}
		return b;
	}

	public int scriptsCount() {
		return DOCUMENT.getElementsByTag("script").size();
	}

	public Elements canonical() {
		return DOCUMENT.getElementsByAttributeValue("rel", "canonical");
	}

	public Elements meta() {
		return DOCUMENT.getElementsByTag("meta");
	}

	public void status(int status) {
		this.STATUS = status;
	}

	public int status() {
		return STATUS;
	}

	public HashSet<String> uniqLinks() {
		return uniqLinks;
	}

	public List<String> links() {
		List<String> links = new ArrayList<>();
		for (Element e : LINKELEMENTS) {
			String url = e.attr("href");
			links.add(url);
		}
		return links;
	}

	public List<String> outLinks() {
		List<String> list = new ArrayList<>();
		list.addAll(outLinks);
		return list;
	}

	public List<String> outDomain() {
		List<String> list = new ArrayList<>();
		for (String url : outLinks()) {
			list.add(UrlUtils.getHost(url));
		}
		return list;
	}

	public List<String> h1() {
		List<String> list = new ArrayList<>();
		for (Element e : DOCUMENT.getElementsByTag("h1")) {
			list.add(e.text());
		}
		return list;
	}

	public List<String> h2() {
		List<String> list = new ArrayList<>();
		for (Element e : DOCUMENT.getElementsByTag("h2")) {
			list.add(e.text());
		}
		return list;
	}

	public List<String> h3() {
		List<String> list = new ArrayList<>();
		for (Element e : DOCUMENT.getElementsByTag("h3")) {
			list.add(e.text());
		}
		return list;
	}

	public List<String> h4() {
		List<String> list = new ArrayList<>();
		for (Element e : DOCUMENT.getElementsByTag("h4")) {
			list.add(e.text());
		}
		return list;
	}

	public List<String> h5() {
		List<String> list = new ArrayList<>();
		for (Element e : DOCUMENT.getElementsByTag("h5")) {
			list.add(e.text());
		}
		return list;
	}

	public Elements hE() {
		Elements h = new Elements();
		for (int i = 1; i < 6; i++) {
			for (Element e : DOCUMENT.getElementsByTag("h" + i)) {
				h.add(e);
			}
		}
		return h;
	}

	public Elements images() {
		return DOCUMENT.getElementsByTag("img");
	}

	public Elements elements() {
		return ELEMENTS;
	}

	public int elementsCount() {
		return ELEMENTS.size();
	}

	public int maxElementDepth() {
		return AnalyticParser.maxElementDepth(ELEMENTS);
	}

	public float avgElementDepth() {
		return AnalyticParser.avgElementDepth(ELEMENTS);
	}

	public float avgChildNodeSize() {
		int s = 0, count = 0;
		for (Element e : ELEMENTS) {
			s = s + e.childNodeSize();
			count++;
		}
		return (float) s / count;
	}

	public Elements linkElements() {
		return LINKELEMENTS;
	}

	public String robotsUrl() {
		return ROBOTSURL;
	}

	public Document doc() {
		return DOCUMENT;
	}

	public String text() {
		return DOCUMENT.text();
	}

	public HashSet<String> uniqWords() {
		HashSet<String> set = new HashSet<>();
		set.addAll(segWords());
		return set;
	}

	public List<String> segWords() {
		List<String> list = new ArrayList<>();
		IKSegmenter ikSegmenter = null;
		Lexeme lexeme;
		ikSegmenter = new IKSegmenter(new StringReader(text()), true);

		try {
			while ((lexeme = ikSegmenter.next()) != null) {
				list.add(lexeme.getLexemeText());
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return list;
	}

	public JSONObject kwAppearances(String kw) {
		JSONObject json = new JSONObject();
		int attMatch = 0;
		int txtMatch = 0;
		int metaMatch = 0;
		int titleMatch = 0;

		for (Element f : meta()) {
			for (Attribute a : f.attributes()) {
				if (a.getValue().contains(kw))
					metaMatch++;
			}
		}

		for (Element e : ELEMENTS) {
			if (e.tagName().equals("title")) {
				titleMatch++;
			} else if (e.ownText().contains(kw)) {
				txtMatch++;
			}

			for (Attribute a : e.empty().attributes()) {
				if (a.getValue().contains(kw))
					attMatch++;
			}
		}

		json.put("attMatch", attMatch);
		json.put("txtMatch", txtMatch);
		json.put("metaMatch", metaMatch);
		json.put("titleMatch", titleMatch);

		return json;
	}

	public String protocol() {
		String protocol = null;
		try {
			protocol = new URL(url()).getProtocol();
		} catch (MalformedURLException e) {

		}
		return protocol;
	}

	public float tf(String kw) {
		return IR.tf(kw, text());
	}

	public List<String> anchors() {
		return IR.anchors(LINKELEMENTS);
	}

	public List<String> boldText() {
		return IR.boldText(ELEMENTS);
	}

	public HashMap<String, ArrayList<String>> listings() {
		return IR.simpleListings(ELEMENTS);
	}

	public boolean isArticlePage() {
		return IR.isArticlePage(doc());
	}

}
